#ifndef TWOPHASESPARRALLELDBOPERATIONMODELGENERATOR1_H_JM_20120910
#define TWOPHASESPARRALLELDBOPERATIONMODELGENERATOR1_H_JM_20120910
/** @file TwoPhasesParrallelHashActionModelGenerator.h
 * Generator jednoprechodoveho algoritmu pre DB operacie.
 **/
#include "ModelGenerator.h"
#include "Slices.h"

namespace jm4bbx {
	/**  Generator modelu pre DB operacie vykonane v jednom priechode.
	 *   Logika vykonania operacie je zalozena na hashovani.
	 *   Vygenerovany model je zlozeny z dvoch faz, kde sa kazda cast 
	 *   paralelizuje podla dodaneho sablonoveho argumentu N.
	 *  
	 *  Priklad vygenerovaneho modelu pre paralelizacny faktor N == 4.
	 * @verbatim                      
	 *                                 hashovanie dat      vyhladavanie dat vacsej           
	 *                                 mensej relacie      relacie v hash. tabulke 
	 *                                  do tabulky         a vykonanie DB operacie  
	 *                               _______##_______         _______##_______         
	 *                              |       ##       |       |       ##       |        
	 *                              |                |       |                |        
	 *            load    start     |_______##_______|       |_______##_______|     stop   output    
	 *    input   data    timer   fork      ##     merge   fork      ##     merge   timer   data   output
	 *     ##______##______##______##               ##______##               ##______##______##______##
	 *     ##      ##      ##      ##               ##      ##               ##      ##      ##      ##
	 *                              |_______##_______|       |_______##_______|        
	 *                              |       ##       |       |       ##       |        
	 *                              |                |       |                |        
	 *                              |_______##_______|       |_______##_______|        
	 *                                      ##                       ##                
	 * @endverbatim                                    
	 * 
	 * @tparam N paralelizacny faktor
	 **/
	template<size_t N>
	class  TwoPhasesParrallelHashActionModelGenerator : public ModelGenerator {
	public:

		std::string typeName_;

		TwoPhasesParrallelHashActionModelGenerator(
			const std::string& inputBoxName,
			const std::string& inputSpecificationR,
			const std::string& inputSpecificationS,
			const std::string& hashBoxName,
			const std::string& actionBoxName,
			const std::string& outputBoxName,
			const std::string& typeName,
			const int operandColumns = 2,
			const int resultColumns = 1)  {

				const int columns = operandColumns + resultColumns;
				typeName_ = typeName;


				BoxDescriptorPtr forkLoadData = addBox("SIGNAL_FORK", genBoxParam("signal_out", 2));
				BoxDescriptorPtr loadDataR = addBox(inputBoxName, 
					genBoxParam("specification", inputSpecificationR)  + 
					genBoxParam("signal_in", 1) + 
					genBoxParam("signal_out", 1));
				BoxDescriptorPtr loadDataS = addBox(inputBoxName,
					genBoxParam("specification", inputSpecificationS) + 
					genBoxParam("signal_in", 1) + 
					genBoxParam("signal_out", 1));
				

				BoxDescriptorPtr startTimer = addBox("START_TIMER", 
					genBoxParam("signal_in", 2) + genBoxParam("signal_out", 1));

				BoxDescriptorPtr fork1 = addBox("Fork", 
					genBoxParam("forkFactor", N) + genBoxParam("signal_in", 1));
				BoxDescriptorPtr hash = addBox(hashBoxName, genBoxParam("signal_out", 1),  N);
				
				//BoxDescriptorPtr merge1 = addBox("SIGNAL_MERGE", genBoxParam("signal_in", N));

				BoxDescriptorPtr fork2 = addBox("Fork", 
					genBoxParam("forkFactor", N) + genBoxParam("signal_in", N));			
				BoxDescriptorPtr action = 
					addBox(actionBoxName, genBoxParam("signal_out", 1), N);
				//BoxDescriptorPtr merge2 = 
				//	addBox("SIGNAL_MERGE", genBoxParam("signal_in", N));

				
				BoxDescriptorPtr timerReport = addBox("TIMER_REPORT", genBoxParam("signal_in", N) + genBoxParam("signal_out", 1));

				BoxDescriptorPtr outData = addBox(outputBoxName, 
					genBoxParam("signal_in", 1) /*+ genBoxParam("Psss", 1)*/);
				
#define SI 1000
#define SO 1000
#define NI 0
#define NO 0

				//--------------------------------------------------------------
				//--------------------------------------------------------------
				//-------------------------------------------------------------- 

				// vstup do vypoctu
				addSlice<PlainSlice<NO,SI>>(getModelInputId(), 
					forkLoadData->getBoxId());
				// rozvetvenie nacitania dat na 2 vetvy (2 relacie R,S)
				addSlice<PlainSlice<SO, SI>>(forkLoadData->getBoxId(), loadDataR->getBoxId());
				addSlice<PlainSlice<SO + 1, SI>>(forkLoadData->getBoxId(), loadDataS->getBoxId());
				// nacitanie data relacie R + signalizovanie startu vypoctu
				// predpoklada sa ze R je mensia a preto sa posiela do prveho
				// priechodu
				addSlice<PlainSlice<SO, SI>>(loadDataR->getBoxId(), startTimer->getBoxId());
				addSlice<PlainSlice<NO, NI>>(loadDataR->getBoxId(), fork1->getBoxId(), 
					genTypes(typeName_, 1));
				// nacitanie relacie S + signalizovanie startu vypoctu
				// predpoklada sa ze S je vacsia a preto sa posiela do druheho 
				// priechodu
				addSlice<PlainSlice<SO, SI + 1>>(loadDataS->getBoxId(), startTimer->getBoxId());
				addSlice<PlainSlice<NO, NI>>(loadDataS->getBoxId(), fork2->getBoxId(), 
					genTypes(typeName_, 1));
				// start timeru == vypoctu ked su nacitane obe relacie R aj S
				addSlice<PlainSlice<SO,SI>>(startTimer->getBoxId(), fork1->getBoxId());
				// prvy priechod == fork, {hash}, merge, signal
				addSlice<ForkSlice<N, NO, NI>>(fork1->getBoxId(), 
					hash->getBoxIds(), genTypes(typeName_, 1));
				addSlice<MergeSlice<N, SO, SI>>(hash->getBoxIds(), fork2->getBoxId());
				
				// druhy priechod == fork, {akcia}, merge, signal
				addSlice<ForkSlice<N, NO, NI>>(fork2->getBoxId(), 
					action->getBoxIds(), genTypes(typeName_, 1));
				addSlice<MergeSlice<N, SO, SI>>(action->getBoxIds(), timerReport->getBoxId());
				// addSlice<MergeSlice<N, NO, NI>>(action->getBoxIds(), outData->getBoxId());		
				// Trosku hack je potrebeny merge bez inkrementovania vstupu.
				// To znamena ze sa vsetkou poleje do jedneho vstup ako to 
				// bude prichadzata nebude sa cakat na vsetko.
				MergeSlice<N, NO, NI>* p = new MergeSlice<N, NO, NI>(action->getBoxIds(), outData->getBoxId(),
					genTypes("string", 1));

				p->setIncrementInArcs(false);
				SliceGeneratorPtr sharedP(p);
				addSlice(sharedP);
				// report timeru -> pokracovanie vypoctu (resp. koniec vypoctu)
				addSlice<PlainSlice<SO, SI>>(timerReport->getBoxId(), outData->getBoxId());
				// koniec vypoctu
				addSlice<PlainSlice<SO, NI>>(outData->getBoxId(), getModelOutputId());

				//addSlice<PlainSlice>(getModelInputId(), loadData->getBoxId());


				//addSlice<PlainSlice>(loadData->getBoxId(), 
				//	startTimer->getBoxId(), genTypes(typeName,	2));

				//addSlice<PlainSlice>(startTimer->getBoxId(), fork1->getBoxId(), 
				//	genTypes(typeName, 2));



				//generateForkActivityMerge(fork1,hash,merge1,fork2, 2, 1);
				//generateForkActivityMerge(fork2,join,merge2, stopTimer, 1, 1);


				//addSlice<PlainSlice>(stopTimer->getBoxId(), 
				//	outData->getBoxId(), genTypes(typeName, 1));

				//addSlice<PlainSlice>(outData->getBoxId(), getModelOutputId());
		}

	private:

		void generateForkActivityMerge(BoxDescriptorPtr& fork, 
			BoxDescriptorPtr& activity, BoxDescriptorPtr& merge, 
			BoxDescriptorPtr& out, int columns, int resultColumns) {

				addSlice<ForkSlice<N>>(fork->getBoxId(), 
					activity->getBoxIds(), genTypes(typeName_, 1));

				addSlice<MergeSlice<N>>(activity->getBoxIds(), 
					merge->getBoxId(), genTypes(typeName_, resultColumns));

				addSlice<PlainSlice>(merge->getBoxId(), out->getBoxId(), 
					genTypes(typeName_, resultColumns));
		}
	};

} // namespace jm4bbx


#endif
